package lelouet.datacenter.simulation.vms;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import lelouet.datacenter.simulation.Event;
import lelouet.datacenter.simulation.events.VMStateChanged;
import lelouet.datacenter.simulation.events.VMStateChanged.VMStates;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * a VM that only has a set of instruction to do.
 * 
 * @author lelouet
 * 
 */
public class HPC extends AVM {

	private static final Logger mlogger = LoggerFactory.getLogger(HPC.class);

	protected long remaininginstructions;

	/**
	 * @param id
	 *            the unique id
	 * @param last
	 *            slot time at creation
	 * @param memrequired
	 *            the size in Mo of the memory this requires to work
	 * @param instructions
	 *            the number of Million of instructions this has to do
	 */
	public HPC(String id, long time, long memrequired, long instructions) {
		super(id, time);
		mlogger
				.debug(
						"creating {} with {} Mo required and {} instructions remaining",
						new Object[] { id, memrequired, instructions });
		setMemUsed(memrequired);
		remaininginstructions = instructions;
	}

	@Override
	public long getMaxCPU() {
		return Long.MAX_VALUE;
	}

	@Override
	public long nextEventTime() {
		long MIPS = getAllocatedCPU();
		if (MIPS < 1) {
			return Long.MAX_VALUE;
		}
		long ret = remaininginstructions / MIPS + getTime();
		mlogger
				.debug(
						"nextEventTime for VM {} with {} allocated MIPS and {} remaining instructions at time {} is {}",
						new Object[] { getId(), getAllocatedCPU(),
								remaininginstructions, getTime(), ret });
		return ret;
	}

	@Override
	public final List<Event> work(long nextStop) {
		long lastTime = getTime();
		long oldremaining = remaininginstructions;
		long MIPS = getAllocatedCPU();
		List<Event> ret = new ArrayList<Event>();
		if ((nextStop - getTime()) * MIPS < remaininginstructions) {
			remaininginstructions -= (nextStop - getTime()) * MIPS;
		} else {
			long endTime = remaininginstructions / MIPS + getTime();
			ret.add(new VMStateChanged(this, endTime, VMStates.RUNNING,
					VMStates.STOPPED));
			remaininginstructions = 0;
			setTime(endTime);
			mlogger.info("{} : job done ", this);
			ret.addAll(onJobDone());
		}
		setTime(nextStop);
		mlogger
				.debug(
						"working from {} till {} with {} allocated MIPs and {} remaining instructions ends at {} with {} remaining instructions ",
						new Object[] { lastTime, nextStop, getAllocatedCPU(),
								oldremaining, getTime(), remaininginstructions });
		return ret;
	}

	/**
	 * what to do when all the work is done. <br />
	 * this has to be specified on children
	 */
	protected List<Event> onJobDone() {
		return Collections.emptyList();
	}

	/** @return all instructions have been done ? */
	public boolean isFinished() {
		return remaininginstructions == 0;
	}

	/** this does not consider any memory allocation. */
	@Override
	public void allocateMemory(long mo) {
	}

	@Override
	public void reconfigure() {
	}
}
